package com.cpms.framework.redis.aspect;

import com.cpms.framework.common.enums.GlobalResponseResultEnum;
import com.cpms.framework.common.exception.BizException;
import com.cpms.framework.redis.annotations.DistributedLock;
import com.cpms.framework.redis.utils.CsLockAspectUtil;
import com.cpms.framework.redis.utils.CsRedissonUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * @description: 分布式锁切面
 * @author: gulang
 * @time: 2021/11/25 16:23
 */
@Aspect
@Configuration
public class DistributedLockAspect {
    private final Logger logger = LoggerFactory.getLogger(DistributedLockAspect.class);
    private static final String DEFAULT_LOCK_KEY_PREFIX = "distributedLock:";
    private static final String DEFAULT_FLAG = "default";


    @Pointcut("@annotation(com.cpms.framework.redis.annotations.DistributedLock)")
    public void distributedLockAspectPoint(){}

    @Around("distributedLockAspectPoint() && @annotation(distributedLock)")
    public Object handelAround(ProceedingJoinPoint point, DistributedLock distributedLock) throws Throwable{
        String prefixKey = distributedLock.prefixKey();
        if (DEFAULT_FLAG.equalsIgnoreCase(prefixKey)) {
            prefixKey = DEFAULT_LOCK_KEY_PREFIX;
        }
        String[] fields = distributedLock.fields();
        Map<String, Object> params = CsLockAspectUtil.getParams(point, fields);
        String lockKey = CsLockAspectUtil.createLockKey(prefixKey, params, point);
        if (!distributedLock.isBlock()) {
            if (Objects.equals(distributedLock.expireTime(), -1L)) {
                if(!CsRedissonUtil.tryLock(lockKey, TimeUnit.MILLISECONDS, 0)) {
                    throw new BizException(GlobalResponseResultEnum.BIZ_BUSY_HANDLE_ERROR.getCode(),
                            distributedLock.lockFailMsg());
                }
            }else{
                if (!CsRedissonUtil.tryLock(lockKey, TimeUnit.MILLISECONDS, 0, distributedLock.expireTime())) {
                    throw new BizException(GlobalResponseResultEnum.BIZ_BUSY_HANDLE_ERROR.getCode(),
                            distributedLock.lockFailMsg());
                }
            }
        }else{
            if (Objects.equals(distributedLock.expireTime(), -1L)) {
                if (!CsRedissonUtil.tryLock(lockKey, TimeUnit.MILLISECONDS, distributedLock.waitTime())) {
                    throw new BizException(GlobalResponseResultEnum.BIZ_BUSY_HANDLE_ERROR.getCode(),
                            distributedLock.lockFailMsg());
                }
            } else {
                if (!CsRedissonUtil.tryLock(lockKey, TimeUnit.MILLISECONDS, distributedLock.waitTime(), distributedLock.expireTime())) {
                    throw new BizException(GlobalResponseResultEnum.BIZ_BUSY_HANDLE_ERROR.getCode(),
                            distributedLock.lockFailMsg());
                }
            }
        }
        try {
            return point.proceed();
        }finally {
            CsRedissonUtil.unlock(lockKey);
        }
    }
}
